#pragma once

#include "type.h"

namespace sfc::reflect {

template <class T>
struct StructField {
  str::Str name;
  T& value;
};

template <class T>
StructField(str::Str, T&) -> StructField<T>;

template <class... U>
struct StructInfo {
  using Fields = tuple::Tuple<StructField<const U>...>;
  Fields _fields;

 public:
  StructInfo(StructField<const U>... fields) : _fields{fields...} {}

  auto fields() const -> const Fields& {
    return _fields;
  }

  auto fields_mut() -> Fields& {
    return _fields;
  }

  void map(auto&& f) const {
    _fields.map(f);
  }

  void map_mut(auto&& f) {
    _fields.map_mut(f);
  }
};

template <class... U>
StructInfo(StructField<const U>...) -> StructInfo<U...>;

}  // namespace sfc::reflect

#define _SFC_STRUCT_FIELD(x) sfc::reflect::StructField{#x, self.x},
#define SFC_STRUCT(T, ...)                                                        \
  static inline auto reflect_struct(const T& self) {                              \
    return sfc::reflect::StructInfo{SFC_FOREACH(_SFC_STRUCT_FIELD, __VA_ARGS__)}; \
  }
